package rok3.projekt.zespver3.client.screens;

import static rok3.projekt.zespver3.client.Settings._window_height;
import static rok3.projekt.zespver3.client.Settings._window_width;
import rok3.projekt.zespver3.client.Resources;
import rok3.projekt.zespver3.client.Settings;
import rok3.projekt.zespver3.client.objects.Character;
import rok3.projekt.zespver3.client.objects.TmpBullet;
import rok3.projekt.zespver3.network.packet.CharacterState__;
import rok3.projekt.zespver3.network.packet.Event__;
import rok3.projekt.zespver3.network.packet.PrimitiveEvent__;
import rok3.projekt.zespver3.network.packet.__AllChanges;
import rok3.projekt.zespver3.network.packet.__CharacterState;
import rok3.projekt.zespver3.network.packet.__Event;
import rok3.projekt.zespver3.network.packet.__PrimitiveEvent;
import rok3.projekt.zespver3.network.packet.__PrimitiveSignal__;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.maps.objects.RectangleMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.math.Circle;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.DelayedRemovalArray;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.IntMap;


public class InGameStage extends Stage {
	private TiledMap map;
	private OrthographicCamera mapCam;
	private OrthogonalTiledMapRenderer mapRenderer;

	private final CharacterState__ state__;
	private Array<Event__> event;
	private Character maincharacter;

	private IntMap<Character> charactermap;
	private __CharacterState mainchar_last_state;

	private DelayedRemovalArray<TmpBullet> bullets;

	private Array<Rectangle> walls;
	private float radius = 15;
	
	/* tmp */
	Vector2 tmp_count_angle = new Vector2();
	private __AllChanges tmp_changes;
	boolean needApplyStateChanges = false;
	float diagonal_speed = MathUtils.cosDeg(45);
	Circle tmp_circle = new Circle();
	private ShapeRenderer shapeRenderer;
	private float newY;
	private float newX;
	
	public InGameStage(short[] player_ids, float width, float height,
			boolean keepAspectRatio) {
		super(width, height, keepAspectRatio);

		this.map = new TmxMapLoader().load("map/map3.tmx");
		this.mapCam = new OrthographicCamera(_window_width, _window_height);
		this.mapCam.translate(_window_width / 2, _window_height / 2);
		this.mapCam.update();

		float unitScale = 1f;
		this.mapRenderer = new OrthogonalTiledMapRenderer(map, unitScale);
		this.mapRenderer.setView(mapCam);

		this.state__ = new CharacterState__();
		state__.id = Resources._updater.my_id;
		this.charactermap = new IntMap<>(player_ids.length);

		for (short id : player_ids) {
			Character tmp;
			if (id == Resources._updater.my_id) {
				tmp = new Character(new Texture(
						Gdx.files.internal("misc/c4.png")), id, 100, 110);
				tmp.setOrigin(17, 17);
				this.setMainCharacter(tmp);
			} else {
				tmp = new Character(new Texture(
						Gdx.files.internal("misc/c1.png")), id, 100, 110);
				tmp.setOrigin(18, 17);
				this.addActor(tmp);
			}
			charactermap.put(id, tmp);
		}
		this.bullets = new DelayedRemovalArray<TmpBullet >(false,32);
		Array<RectangleMapObject> recBounds = map.getLayers().get(3)
				.getObjects().getByType(RectangleMapObject.class);

		walls = new Array<Rectangle>(recBounds.size);
		for (RectangleMapObject r: recBounds) {
			walls.add(r.getRectangle());
		}
		shapeRenderer = new ShapeRenderer();
		
		event = new Array<Event__>(false,16);
	}

	
	public void setMainCharacter(Character maincharacter) {
		if (maincharacter == null)
			throw new GdxRuntimeException("maincharacter can not be null");
		this.maincharacter = maincharacter;
	}

	public void addMainCharacterToStage() {
		this.addActor(maincharacter);
	}

	public void catchInput(float delta) {
		/*
		 *  need to expand
		 */
		
		float delta_move = delta * maincharacter.getSpeed();
		
		float dx = 0, dy = 0;
		if (Gdx.input.isKeyPressed(Keys.A)) {
			dx--;
		}
		if (Gdx.input.isKeyPressed(Keys.D)) {
			dx++;
		}
		if (Gdx.input.isKeyPressed(Keys.W)) {
			dy++;
		}
		if (Gdx.input.isKeyPressed(Keys.S)) {
			dy--;
		}
		if (dx * dy != 0) {
			dx *= diagonal_speed;
			dy *= diagonal_speed;
		}
		
		newX=maincharacter.getX() + dx * delta_move;
		newY= maincharacter.getY()+ dy* delta_move;
		
		/*
		 * Mouse input
		 */
		
		
	}

	
	private void doPhysic (float delta) {
		for (Rectangle r : walls ) {
			bullets.begin();
			for ( rok3.projekt.zespver3.client.objects.TmpBullet b: this.bullets ) {
				if ( r.contains(b.getVisualPositionBottomleft()) 
						|| r.contains(b.getVisualPositionBottomright())
						|| r.contains(b.getVisualPositionTopleft())
						|| r.contains(b.getVisualPositionTopright())) {
					b.remove();
					bullets.removeValue(b, true);
				}
			}
			bullets.end();
		}

		tmp_circle.set(newX+ maincharacter.getOriginX(),newY + maincharacter.getOriginY(), radius);
		
		float k;
		for (Rectangle r : walls) {
			if (Intersector.overlaps(tmp_circle, r)) {
				if (  newX+maincharacter.getOriginX() < r.x ) {
					System.out.print("case1 ");
					k = Intersector.distanceLinePoint(r.x, r.y, r.x, r.y+r.height, newX+maincharacter.getOriginX(), newY+maincharacter.getOriginY());
					newX -= (radius-k);
				} else if (  newX+maincharacter.getOriginX() + radius*2> r.x + r.width ) {
					System.out.print("case2 ");
					k = Intersector.distanceLinePoint(r.x+r.width, r.y, r.x+r.width, r.y+r.height, newX+maincharacter.getOriginX(), newY+maincharacter.getOriginY());
					newX += (radius-k);
				}
				if (  newY+maincharacter.getOriginY() < r.y  ) {
					System.out.print("case3 ");
					k =  Intersector.distanceLinePoint(r.x, r.y, r.x+r.width, r.y, newX+maincharacter.getOriginX(), newY+maincharacter.getOriginY());
					newY -= (radius-k);
				} else if ( newY+maincharacter.getOriginY() + radius*2 > r.y + r.height) {
					System.out.print("case4 ");
					k = Intersector.distanceLinePoint(r.x, r.y+r.height, r.x+r.width, r.y+r.height, newX+maincharacter.getOriginX(), newY+maincharacter.getOriginY());
					newY += (radius-k);
				}
			}
		}
		
		maincharacter.setPosition(newX, newY);
		
		
		state__.x = newX;
		state__.y = newY;
		state__.rotation = computeAngle(Gdx.input.getX(), _window_height-Gdx.input.getY(),
				maincharacter.getOriginX() + maincharacter.getX(),
				maincharacter.getOriginY() + maincharacter.getY());
		state__.local_timestamp += delta;

		
	}
	@Override
	public boolean touchDown(int screenX, int screenY, int pointer, int button) {
		createBullet(maincharacter);
		PrimitiveEvent__ e = new PrimitiveEvent__();
		e.type = PrimitiveEvent__.Type__.shoot__;
		event.add(e);
		return super.touchDown(screenX, screenY, pointer, button);
	}
	
	private float computeAngle(float cursor_x, float cursor_y, float origin_x,
			float origin_y) {
		return tmp_count_angle.set(cursor_x - origin_x, cursor_y - origin_y)
				.angle();
	}

	public CharacterState__ getState() {
		return state__;
	}
	
	
	Vector2 tmp2=new Vector2();
	@Override
	public void act(float delta) {
		if (needApplyStateChanges) {
			for (__CharacterState st : tmp_changes.states) {
				if (st.id == Resources._updater.my_id) {
					mainchar_last_state = st;
					continue;
				}
				Character k = charactermap.get(st.id);
				k.clearActions();
				float movetime = 60*delta*k.getSpeed()/(tmp2.set(k.getX(),k.getY()).dst2(st.x,st.y)+1);
				movetime = movetime > 1 ? 1: movetime;
				k.addAction(Actions.moveBy(st.x - k.getX(), st.y - k.getY(),movetime));
				k.addAction(Actions.rotateBy(st.rotation-k.getRotation(), (float) (0.002*Math.abs(st.rotation- k.getRotation()))));
				k.setHp(st.hp);
			}
			
			for (__Event p : tmp_changes.events ) {
				if ( p instanceof __PrimitiveEvent ) {
					__PrimitiveEvent e = (__PrimitiveEvent) p;
					if (e.source_id == Resources._updater.my_id) {
						continue;
					}
					Character k = charactermap.get(e.source_id);
					createBullet(k);
				}
			}
			
			needApplyStateChanges = false;
		}
		maincharacter.setPosition(state__.x, state__.y);
		maincharacter.setRotation(state__.rotation);
		
		super.act(delta);
		
		this.doPhysic(delta);

	}
	
	private void createBullet (Character source ) {
		TmpBullet bullet = new TmpBullet(
				Resources._bullet_texture,
				//new Texture(Gdx.files.internal("data/bullet.png")), 
				source, 
				source.getX()+58, 
				source.getY()+11.5f-Resources._bullet_texture.getHeight()/2,
				-(58-source.getOriginX()),
				-(11.5f-Resources._bullet_texture.getHeight()/2-source.getOriginY()), 
				source.getRotation(),
				800);
		bullets.add(bullet);
		this.addActor(bullet);
	}
	
	public Event__[] getEvents () {
		Event__[] e = new Event__[event.size];
		for(short i = 0; i < event.size; i++ ) {
			e[i] = event.pop();
		}
		return e;
	}
	
	@Override
	public void draw() {
		mapRenderer.render();
		super.draw();

		if (!Settings._debug_on ) return;
		
		shapeRenderer.setProjectionMatrix(getCamera().combined);

		shapeRenderer.begin(ShapeType.Line);
		shapeRenderer.setColor(1, 1, 0, 1);
		for (Rectangle r : walls) {
			shapeRenderer.rect(r.x, r.y, r.width, r.height);
		}
		for ( TmpBullet b : bullets ) {
			shapeRenderer.rect(b.getX(), b.getY(), b.getWidth()*b.getScaleX(), b.getHeight()*b.getScaleY(), b.getOriginX(), b.getOriginY(), b.getRotation(), Color.RED, Color.RED, Color.RED, Color.RED);
		}
		shapeRenderer.circle(maincharacter.getX()+maincharacter.getOriginX(), maincharacter.getY()+maincharacter.getOriginY(), radius);
		shapeRenderer.end();
	}

	public void applyUpdatesFromServer(__AllChanges __changes) {
		this.tmp_changes = __changes;
		this.needApplyStateChanges = true;
	}

}
